﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NFit.Core.Errors;
using NFit.Core.Interfaces;

namespace NFit.Core.Fixtures
{
    /// <summary>
    /// Base class for fixtures relying on strict tabular formats like ColumnFixture or SetupFixture
    /// </summary>
    public abstract class TableFixture : AbstractFixture
    {
        /// <summary>
        /// interpret the fixture definition (e.g compute arguments and definition)
        /// </summary>
        /// <returns></returns>
        public override void Resolve()
        {
            FixtureDefinition.Definition = FixtureDefinition[0];
            FixtureDefinition.RemoveAt(0); // Remove column definition
        }

        /// <summary>
        /// Treat a table cell
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="defName"></param>
        /// <returns></returns>
        protected virtual bool? ResolveCell(FixtureCell cell, string defName)
        {
            return ReflectionHelper.SetFieldOrProperty(Context, this, cell, defName);
        }

        /// <summary>
        /// Execute the fixture from the provided row
        /// </summary>
        /// <returns></returns>
        public override bool Run()
        {
            Row.Status.Success = CheckFormat();
            if (!Row.IsSuccessful)
                return false;

            AbstractFixture fixtureImpl = FixtureDefinition.GetImplementation();
            BeforeRun();

            for (int i = 0; i < FixtureDefinition.Definition.Count; i++)
            {
                FixtureCell cellDef = FixtureDefinition.Definition[i];
                FixtureCell cell = Row[i];

                RunCell(fixtureImpl, cell, cellDef);

                Row.Status.Success = Row.Status.Success.Value && (!cell.Status.Success.HasValue || cell.Status.Success.Value);
            } 

            AfterRun();

            return Row.Status.Success.Value;
        }

        private void RunCell(AbstractFixture fixtureImpl, FixtureCell cell, FixtureCell cellDef)
        {
            try
            {
                cell.Status.Success = ResolveCell(cell, cellDef.Text);                    
            }
            catch(MethodNotFoundException)
            {
                cell.Status.NestedError = ElementNotFoundError.Method(cellDef.Text,this.GetType().FullName);
                cell.Status.Success = false;
                Row.Status.Success = false;
            }
            catch(Exception ex)
            {
                cell.Status.NestedError = new UnexpectedError(ex);
                cell.Status.Success = false;
                Row.Status.Success = false;
            }
        }

        private bool CheckFormat()
        {
            if (FixtureDefinition.Definition.Count > Row.Count)
            {
                Row.Status.Success = false;
                Row.Status.NestedError = new InvalidFixtureFormatError(FixtureFormatErrorKind.NumberOfCellsInvalid);
                return false;
            }
            return true;
        }

        /// <summary>
        /// Method called before running the row
        /// </summary>
        protected virtual void BeforeRun()
        {

        }

        /// <summary>
        /// Method called after running the row
        /// </summary>
        protected virtual void AfterRun()
        {
            
        }
    }
}
